<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<script src="./jquery-3.5.1.min.js"></script>
		<link type="text/css" href="./style.css" rel="stylesheet" />
		<style>
			html,
			body {
				width: 100%;
				height: 100%;
				margin: 0;
				padding: 0;
				overflow: hidden;
			}
			.container {
				width: 100%;
				height: 100%;
				margin: 0;
				padding: 0;
				background-color: #000000;
			}
		</style>
	</head>
	<body>
		<audio autoplay="autopaly">
			<source src="./我爱你.mp3" type="audio/mp3" loop="loop" />
		</audio>
		<div id="jsi-cherry-container" class="container">
			<div class="box">
				<ul class="minbox">
					<li><img src="./w1.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./m1.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./w2.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./m2.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./w3.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./m3.jpg" / style="width: 100%; height: 100%;"></li>
				</ul>
				<ol class="maxbox">
					<li><img src="./w1.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./m1.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./w2.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./m2.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./w3.jpg" / style="width: 100%; height: 100%;"></li>
					<li><img src="./m3.jpg" / style="width: 100%; height: 100%;"></li>
				</ol>
			</div>
		</div>

		<script>
			var RENDERER = {
				INIT_CHERRY_BLOSSOM_COUNT: 30,
				MAX_ADDING_INTERVAL: 10,

				init: function () {
					this.setParameters();
					this.reconstructMethods();
					this.createCherries();
					this.render();
					if (
						navigator.userAgent.match(
							/(phone|pod|iPhone|iPod|ios|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
						)
					) {
						var box = document.querySelectorAll(".box")[0];
						console.log(box, "移动端");
						box.style.marginTop = "65%";
					}
				},
				setParameters: function () {
					this.$container = $("#jsi-cherry-container");
					this.width = this.$container.width();
					this.height = this.$container.height();
					this.context = $("<canvas />")
						.attr({ width: this.width, height: this.height })
						.appendTo(this.$container)
						.get(0)
						.getContext("2d");
					this.cherries = [];
					this.maxAddingInterval = Math.round(
						(this.MAX_ADDING_INTERVAL * 1000) / this.width
					);
					this.addingInterval = this.maxAddingInterval;
				},
				reconstructMethods: function () {
					this.render = this.render.bind(this);
				},
				createCherries: function () {
					for (
						var i = 0,
							length = Math.round(
								(this.INIT_CHERRY_BLOSSOM_COUNT * this.width) / 1000
							);
						i < length;
						i++
					) {
						this.cherries.push(new CHERRY_BLOSSOM(this, true));
					}
				},
				render: function () {
					requestAnimationFrame(this.render);
					this.context.clearRect(0, 0, this.width, this.height);

					this.cherries.sort(function (cherry1, cherry2) {
						return cherry1.z - cherry2.z;
					});
					for (var i = this.cherries.length - 1; i >= 0; i--) {
						if (!this.cherries[i].render(this.context)) {
							this.cherries.splice(i, 1);
						}
					}
					if (--this.addingInterval == 0) {
						this.addingInterval = this.maxAddingInterval;
						this.cherries.push(new CHERRY_BLOSSOM(this, false));
					}
				},
			};
			var CHERRY_BLOSSOM = function (renderer, isRandom) {
				this.renderer = renderer;
				this.init(isRandom);
			};
			CHERRY_BLOSSOM.prototype = {
				FOCUS_POSITION: 300,
				FAR_LIMIT: 600,
				MAX_RIPPLE_COUNT: 100,
				RIPPLE_RADIUS: 100,
				SURFACE_RATE: 0.5,
				SINK_OFFSET: 20,

				init: function (isRandom) {
					this.x = this.getRandomValue(
						-this.renderer.width,
						this.renderer.width
					);
					this.y = isRandom
						? this.getRandomValue(0, this.renderer.height)
						: this.renderer.height * 1.5;
					this.z = this.getRandomValue(0, this.FAR_LIMIT);
					this.vx = this.getRandomValue(-2, 2);
					this.vy = -2;
					this.theta = this.getRandomValue(0, Math.PI * 2);
					this.phi = this.getRandomValue(0, Math.PI * 2);
					this.psi = 0;
					this.dpsi = this.getRandomValue(Math.PI / 600, Math.PI / 300);
					this.opacity = 0;
					this.endTheta = false;
					this.endPhi = false;
					this.rippleCount = 0;

					var axis = this.getAxis(),
						theta =
							this.theta +
							(Math.ceil(
								-(this.y + this.renderer.height * this.SURFACE_RATE) / this.vy
							) *
								Math.PI) /
								500;
					theta %= Math.PI * 2;

					this.offsetY =
						40 * (theta <= Math.PI / 2 || theta >= (Math.PI * 3) / 2 ? -1 : 1);
					this.thresholdY =
						this.renderer.height / 2 +
						this.renderer.height * this.SURFACE_RATE * axis.rate;
					this.entityColor = this.renderer.context.createRadialGradient(
						0,
						40,
						0,
						0,
						40,
						80
					);
					this.entityColor.addColorStop(
						0,
						"hsl(330, 70%, " + 50 * (0.3 + axis.rate) + "%)"
					);
					this.entityColor.addColorStop(
						0.05,
						"hsl(330, 40%," + 55 * (0.3 + axis.rate) + "%)"
					);
					this.entityColor.addColorStop(
						1,
						"hsl(330, 20%, " + 70 * (0.3 + axis.rate) + "%)"
					);
					this.shadowColor = this.renderer.context.createRadialGradient(
						0,
						40,
						0,
						0,
						40,
						80
					);
					this.shadowColor.addColorStop(
						0,
						"hsl(330, 40%, " + 30 * (0.3 + axis.rate) + "%)"
					);
					this.shadowColor.addColorStop(
						0.05,
						"hsl(330, 40%," + 30 * (0.3 + axis.rate) + "%)"
					);
					this.shadowColor.addColorStop(
						1,
						"hsl(330, 20%, " + 40 * (0.3 + axis.rate) + "%)"
					);
				},
				getRandomValue: function (min, max) {
					return min + (max - min) * Math.random();
				},
				getAxis: function () {
					var rate = this.FOCUS_POSITION / (this.z + this.FOCUS_POSITION),
						x = this.renderer.width / 2 + this.x * rate,
						y = this.renderer.height / 2 - this.y * rate;
					return { rate: rate, x: x, y: y };
				},
				renderCherry: function (context, axis) {
					context.beginPath();
					context.moveTo(0, 40);
					context.bezierCurveTo(-60, 20, -10, -60, 0, -20);
					context.bezierCurveTo(10, -60, 60, 20, 0, 40);
					context.fill();

					for (var i = -4; i < 4; i++) {
						context.beginPath();
						context.moveTo(0, 40);
						context.quadraticCurveTo(i * 12, 10, i * 4, -24 + Math.abs(i) * 2);
						context.stroke();
					}
				},
				render: function (context) {
					var axis = this.getAxis();

					if (
						axis.y == this.thresholdY &&
						this.rippleCount < this.MAX_RIPPLE_COUNT
					) {
						context.save();
						context.lineWidth = 2;
						context.strokeStyle =
							"hsla(0, 0%, 100%, " +
							(this.MAX_RIPPLE_COUNT - this.rippleCount) /
								this.MAX_RIPPLE_COUNT +
							")";
						context.translate(
							axis.x +
								this.offsetY * axis.rate * (this.theta <= Math.PI ? -1 : 1),
							axis.y
						);
						context.scale(1, 0.3);
						context.beginPath();
						context.arc(
							0,
							0,
							(this.rippleCount / this.MAX_RIPPLE_COUNT) *
								this.RIPPLE_RADIUS *
								axis.rate,
							0,
							Math.PI * 2,
							false
						);
						context.stroke();
						context.restore();
						this.rippleCount++;
					}
					if (axis.y < this.thresholdY || !this.endTheta || !this.endPhi) {
						if (this.y <= 0) {
							this.opacity = Math.min(this.opacity + 0.01, 1);
						}
						context.save();
						context.globalAlpha = this.opacity;
						context.fillStyle = this.shadowColor;
						context.strokeStyle =
							"hsl(330, 30%," + 40 * (0.3 + axis.rate) + "%)";
						context.translate(
							axis.x,
							Math.max(axis.y, this.thresholdY + this.thresholdY - axis.y)
						);
						context.rotate(Math.PI - this.theta);
						context.scale(axis.rate * -Math.sin(this.phi), axis.rate);
						context.translate(0, this.offsetY);
						this.renderCherry(context, axis);
						context.restore();
					}
					context.save();
					context.fillStyle = this.entityColor;
					context.strokeStyle = "hsl(330, 40%," + 70 * (0.3 + axis.rate) + "%)";
					context.translate(
						axis.x,
						axis.y + Math.abs(this.SINK_OFFSET * Math.sin(this.psi) * axis.rate)
					);
					context.rotate(this.theta);
					context.scale(axis.rate * Math.sin(this.phi), axis.rate);
					context.translate(0, this.offsetY);
					this.renderCherry(context, axis);
					context.restore();

					if (this.y <= -this.renderer.height / 4) {
						if (!this.endTheta) {
							for (
								var theta = Math.PI / 2, end = (Math.PI * 3) / 2;
								theta <= end;
								theta += Math.PI
							) {
								if (this.theta < theta && this.theta + Math.PI / 200 > theta) {
									this.theta = theta;
									this.endTheta = true;
									break;
								}
							}
						}
						if (!this.endPhi) {
							for (
								var phi = Math.PI / 8, end = (Math.PI * 7) / 8;
								phi <= end;
								phi += (Math.PI * 3) / 4
							) {
								if (this.phi < phi && this.phi + Math.PI / 200 > phi) {
									this.phi = Math.PI / 8;
									this.endPhi = true;
									break;
								}
							}
						}
					}
					if (!this.endTheta) {
						if (axis.y == this.thresholdY) {
							this.theta +=
								(Math.PI / 200) *
								(this.theta < Math.PI / 2 ||
								(this.theta >= Math.PI && this.theta < (Math.PI * 3) / 2)
									? 1
									: -1);
						} else {
							this.theta += Math.PI / 500;
						}
						this.theta %= Math.PI * 2;
					}
					if (this.endPhi) {
						if (this.rippleCount == this.MAX_RIPPLE_COUNT) {
							this.psi += this.dpsi;
							this.psi %= Math.PI * 2;
						}
					} else {
						this.phi += Math.PI / (axis.y == this.thresholdY ? 200 : 500);
						this.phi %= Math.PI;
					}
					if (this.y <= -this.renderer.height * this.SURFACE_RATE) {
						this.x += 2;
						this.y = -this.renderer.height * this.SURFACE_RATE;
					} else {
						this.x += this.vx;
						this.y += this.vy;
					}
					return (
						this.z > -this.FOCUS_POSITION &&
						this.z < this.FAR_LIMIT &&
						this.x < this.renderer.width * 1.5
					);
				},
			};
			$(function () {
				RENDERER.init();
			});
		</script>
	</body>
</html>
